package com.onlyoffice.integration.documentserver.storage;

import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.util.CollectionUtils;
import com.onlyoffice.integration.config.S3Properties;
import com.onlyoffice.integration.services.S3FileService;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Classname S3LocalFileStore
 * @Description S3LocalFileStore
 * @Date 2023/8/7 11:30
 * @Author seven002
 */
@Log4j2
@ConditionalOnBean(S3Properties.class)
@Component
public class S3LocalFileStore implements FileStorageMutator, FileStoragePathBuilder {

    @Autowired
    private S3FileService s3FileService;
    @Autowired
    private S3Properties s3Properties;

    @Value("${files.docservice.history.postfix}")
    private String hisPostfix;

    @Value("${files.docservice.url.example}")
    private String docserviceUrlExample;
    @Autowired
    private HttpServletRequest request;

    private static final Pattern versionPattern = Pattern.compile("/(\\d+)/");


    @Override
    public void createDirectory(Path path) {
        // 不需要生成文件夹
    }

    @SneakyThrows
    @Override
    public boolean createFile(Path path, InputStream stream) {
        String fileName = dealHisName(path);
        log.info("createFile...fileName={},size={}", fileName, stream.available());
        s3FileService.putObject(s3Properties.getBucketName(), fileName, stream);
        return true;
    }

    @Override
    public boolean deleteFile(String fileName) {
        if(StringUtils.isBlank(fileName)){
            return true;
        }
        fileName = dealHisName(Paths.get(fileName));
        log.info("deleteFile...fileName={}", fileName);
        s3FileService.deleteObject(s3Properties.getBucketName(), fileName);
        return true;
    }

    @Override
    public boolean deleteFileHistory(String fileName) {
        List<S3ObjectSummary> allMatchObj = s3FileService.getAllObjectsByPrefix(s3Properties.getBucketName(), fileName, true);
        if (CollectionUtils.isNullOrEmpty(allMatchObj)) {
            return true;
        }
        allMatchObj.forEach(o -> {
            s3FileService.deleteObject(s3Properties.getBucketName(), o.getKey());
        });
        return true;
    }

    @Override
    public String updateFile(String fileName, byte[] bytes) {
        s3FileService.putObject(s3Properties.getBucketName(), fileName,
                new ByteArrayInputStream(bytes));
        return fileName;
    }

    @Override
    public boolean writeToFile(String pathName, String payload) {
        pathName = dealHisName(Paths.get(pathName));
        byte[] bytes = payload.getBytes(StandardCharsets.UTF_8);
        s3FileService.putObject(s3Properties.getBucketName(), pathName,
                new ByteArrayInputStream(bytes));
        return true;
    }

    @Override
    public boolean moveFile(Path source, Path destination) {
        String srcName = source.getFileName().toString();
        String dest = destination.toString();
        if (dest.contains(srcName + hisPostfix + File.separator)) {
            String desName = dest.substring(dest.lastIndexOf(srcName));
            dest = desName.replaceAll("\\\\", "/");
        }
        s3FileService.copyObject(s3Properties.getBucketName(), srcName, dest);
        return true;
    }

    @Override
    public Resource loadFileAsResource(String fileName) {
        boolean exist = s3FileService.checkObjectExist(s3Properties.getBucketName(), fileName);
        if (!exist) {
            return null;
        }
        try {
            String path = s3FileService.generatePublicUrl(s3Properties.getBucketName(), fileName);
            Resource resource = new UrlResource(path);
            if (resource.exists()) {
                return resource;
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    @Override
    public Resource loadFileAsResourceHistory(String fileName, String version, String file) {
        String hisFileName = fileName + hisPostfix + File.separator + version
                + File.separator + file;
        return this.loadFileAsResource(hisFileName);
    }

    @Override
    public File[] getStoredFiles() {
        List<S3ObjectSummary> allObjectsByPrefix = s3FileService.getAllObjectsByPrefix(s3Properties.getBucketName(), "", false);
        if (CollectionUtils.isNullOrEmpty(allObjectsByPrefix)) {
            return new File[0];
        }
        List<File> collect = allObjectsByPrefix.stream().map(S3ObjectSummary::getKey)
                .filter(key -> !key.contains(hisPostfix))
                .map(key -> new File(key)).collect(Collectors.toList());
        return collect.toArray(new File[collect.size()]);
    }

    @Override
    public void createMeta(String fileName, String uid, String uname) {
        String lastFileName = fileName + hisPostfix + "/" + "createdInfo.json";
        JSONObject json = new JSONObject();
        json.put("created", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                .format(new Date()));
        json.put("id", uid);
        json.put("name", uname);
        String payload = json.toJSONString();
        this.writeToFile(lastFileName, payload);
    }

    // 没有就创建，有就更新
    @SneakyThrows
    @Override
    public boolean createOrUpdateFile(Path path, InputStream stream) {
        return this.createFile(path, stream);
    }

    @Override
    public void configure(String address) {
        // oss 没有文件夹的概念，不做处理
    }

    @Override
    public String getStorageLocation() {
        // oss 没有文件夹的概念，不做处理
        return File.separator + "office" + File.separator + "store";
    }

    @Override
    public String getFileLocation(String fileName) {
        return fileName;
    }

    @Override
    public String getServerUrl(Boolean forDocumentServer) {
        if (forDocumentServer && !docserviceUrlExample.equals("")) {
            return docserviceUrlExample;
        } else {
            return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
                    + request.getContextPath();
        }
    }

    @Override
    public String getHistoryDir(String fileName) {
        return fileName + hisPostfix;
    }

    // 获取文件的最新版本
    // historyPath 文件名 ifIndexPage=true
    @Override
    public int getFileVersion(String fileName, Boolean ifIndexPage) {
        log.info("getFileVersion... historyPath={},ifIndexPage={}", fileName, ifIndexPage);
        fileName = this.dealFileName(fileName);

        List<S3ObjectSummary> allObjectsByPrefix = s3FileService.getAllObjectsByPrefix(s3Properties.getBucketName(), fileName, false);
        if (org.springframework.util.CollectionUtils.isEmpty(allObjectsByPrefix)) {
            return 0;
        }
        List<String> keys = allObjectsByPrefix.stream().map(S3ObjectSummary::getKey).collect(Collectors.toList());
        int version = CalculateVersion(keys);
        log.info("getFileVersion... version is {}", version);
        return version;
    }


    @Override
    public String getForcesavePath(String fileName, Boolean create) {
        return "";
    }

    @Override
    public String readFileToEnd(String dir, String fileName) {
        S3Object object = s3FileService.getObject(s3Properties.getBucketName(), dir + File.separator + fileName);
        byte[] bytes;
        try {
            int available = object.getObjectContent().available();
            bytes = new byte[available];
            object.getObjectContent().read(bytes);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return new String(bytes, StandardCharsets.UTF_8);
    }

    @Override
    public boolean fileExit(Path path) {
        return s3FileService.checkObjectExist(s3Properties.getBucketName(), path.getFileName().toString());
    }

    @Override
    public long getFileLastModified(String fileName){
        ObjectMetadata objectMate = s3FileService.getObjectMate(s3Properties.getBucketName(), fileName);
        return objectMate.getLastModified().getTime();
    }


    private int CalculateVersion(List<String> keys) {
        int version = 1;
        for (String key : keys) {
            Matcher matcher = versionPattern.matcher(key);
            if (matcher.find()) {
                int num = Integer.parseInt(matcher.group(1)) + 1;
                version = Math.max(num, version);
            }
        }
        return version;
    }

    private String dealFileName(String str) {
        String substring = str.substring(str.lastIndexOf(File.separator) + 1);
        System.out.println(substring);
        if (substring.endsWith("-hist")) {
            return substring.substring(0, substring.length() - 5);
        }
        return substring;
    }

    private String dealHisName(Path path) {
        String ss = File.separator.equals("/") ? "/" : "\\\\";
        String str = path.toString();
        if (str.contains("-hist" + File.separator)) {
            String s1 = Stream.of(str.split(ss)).filter(s -> s.contains("-hist")).findFirst().get();
            return str.substring(str.lastIndexOf(s1));
        }
        return path.getFileName().toString();
    }


}
